package xuecheng.tool.api.handler;

import java.io.Serializable;
import java.util.stream.Collectors;

import javax.validation.ConstraintViolationException;

import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import xuecheng.tool.api.dto.ApiResultDTO;
import xuecheng.tool.json.JsonUtils;
import xuecheng.tool.json.JsonUtilsFactory;

/**
 * 全局异常处理
 *
 * @author PengXueCheng
 * @version 1.0.0
 */
@Slf4j
// @ControllerAdvice
public class CpxGlobalExceptionHandler {

    private JsonUtils jsonUtils = JsonUtilsFactory.obtain();

    /**
     * 处理请求参数校验错误异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler({ConstraintViolationException.class, MethodArgumentNotValidException.class})
    @ResponseBody
    public ApiResultDTO<Object> violationException(Exception e) {
        if (e instanceof ConstraintViolationException) {
            return constraintViolationException((ConstraintViolationException)e);
        } else if (e instanceof MethodArgumentNotValidException) {
            return methodArgumentNotValidException((MethodArgumentNotValidException)e);
        } else {
            ApiResultDTO<Object> result = ApiResultDTO.badRequest(null, null);
            log.error("violationException|{}|{}", result, e.getMessage());
            return result;
        }
    }

    /**
     * 处理所有异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ApiResultDTO<Object> exception(Exception e) {
        ApiResultDTO<Object> result = ApiResultDTO.internalServerError(null);
        log.error("exception|{}|{}", result, e.getMessage());
        return result;
    }

    private ApiResultDTO<Object> constraintViolationException(ConstraintViolationException e) {
        ApiResultDTO<Object> result = ApiResultDTO.badRequest(null, e.getMessage());
        log.error("constraintViolationException|{}|{}", result, e.getMessage());
        return result;
    }

    private ApiResultDTO<Object> methodArgumentNotValidException(MethodArgumentNotValidException e) {
        String error = e.getBindingResult().getFieldErrors().stream()
            .map(fieldError -> jsonUtils.toJson(FieldErrorEntity.builder().field(fieldError.getField())
                .defaultMessage(fieldError.getDefaultMessage()).rejectedValue(fieldError.getRejectedValue()).build()))
            .collect(Collectors.joining("|"));

        ApiResultDTO<Object> result = ApiResultDTO.badRequest(null, error);
        log.error("methodArgumentNotValidException|{}|{}", result, e.getMessage());
        return result;
    }

    @Data
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    private static class FieldErrorEntity implements Serializable {

        /**
         * 返回对象的受影响字段。
         */
        private String field;

        /**
         * 错误默认提示信息
         */
        private String defaultMessage;

        /**
         * 返回拒绝的字段值。
         */
        private Object rejectedValue;

    }

}
